{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "V0OcBOvipOP8"
   },
   "source": [
    "# Parachute Drop from Helicopter\n",
    "\n",
    "A Monte Carlo Dispersion Analysis of a Parachute Drop from Helicopter using RocketPy\n",
    "\n",
    "This is an advanced use of RocketPy. This notebook wraps RocketPy's methods to run a Monte Carlo analysis and predict probability distributions of the rocket's landing point if realeased from a helicopter. This is a common test used to validate the parachute system before a rocket launch."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "TODO: Use the new MonteCarlo class in this notebook"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Clone data files if using Google Colab\n",
    "\n",
    "If you are running this using Binder, or you are running locally with the necessary files, you do not need to run this.\n",
    "On the other hand, if you are running on Google Colab, make sure to run the cell below to download the necessary files."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!curl -o dispersion_analysis_inputs/Cd_PowerOff.csv --create-dirs https://raw.githubusercontent.com/RocketPy-Team/RocketPy/master/docs/notebooks/dispersion_analysis/dispersion_analysis_inputs/Cd_PowerOff.csv\n",
    "!curl -o dispersion_analysis_inputs/Cd_PowerOn.csv --create-dirs https://raw.githubusercontent.com/RocketPy-Team/RocketPy/master/docs/notebooks/dispersion_analysis/dispersion_analysis_inputs/Cd_PowerOn.csv\n",
    "!curl -o dispersion_analysis_inputs/LASC2019_reanalysis.nc --create-dirs https://raw.githubusercontent.com/RocketPy-Team/RocketPy/master/docs/notebooks/dispersion_analysis/dispersion_analysis_inputs/LASC2019_reanalysis.nc\n",
    "!curl -o dispersion_analysis_inputs/thrustCurve.csv --create-dirs https://raw.githubusercontent.com/RocketPy-Team/RocketPy/master/docs/notebooks/dispersion_analysis/dispersion_analysis_inputs/thrustCurve.csv\n",
    "!curl -o dispersion_analysis_inputs/Valetudo_basemap_final.jpg --create-dirs https://raw.githubusercontent.com/RocketPy-Team/RocketPy/master/docs/notebooks/dispersion_analysis/dispersion_analysis_inputs/Valetudo_basemap_final.jpg\n",
    "!mkdir -p dispersion_analysis_outputs"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "Um2fvNlQpTAH"
   },
   "source": [
    "## Install and Load Necessary Libraries\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "JJNfsYrwpXGJ",
    "outputId": "e2c4e1ef-4720-40ae-abd1-4d2a599bedd4"
   },
   "outputs": [],
   "source": [
    "!pip install rocketpy"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "rNY7u8fApOP_"
   },
   "outputs": [],
   "source": [
    "import glob\n",
    "from datetime import datetime\n",
    "from time import perf_counter, process_time, time\n",
    "\n",
    "import numpy as np\n",
    "from IPython.display import display\n",
    "from numpy.random import choice, normal, uniform\n",
    "\n",
    "from rocketpy import Environment, Flight, Function, Rocket, SolidMotor"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Next, we import matplotlib to produce awesome looking plots."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "0uEmvBIt5Ltg"
   },
   "outputs": [],
   "source": [
    "%config InlineBackend.figure_formats = ['svg']\n",
    "import matplotlib as mpl\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "%matplotlib inline\n",
    "mpl.rcParams[\"figure.figsize\"] = [8, 5]\n",
    "mpl.rcParams[\"figure.dpi\"] = 120\n",
    "mpl.rcParams[\"font.size\"] = 14\n",
    "mpl.rcParams[\"legend.fontsize\"] = 14\n",
    "mpl.rcParams[\"figure.titlesize\"] = 14"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "4ksmBqU7pOQC"
   },
   "source": [
    "## Defining Analysis Parameters\n",
    "\n",
    "The analysis parameters are a collection of expected values (and their uncertainties, or standard deviation) that completely defines a rocket flight.\n",
    "As an assumption, the parameters which define the flight can behave in 3 different ways:\n",
    " - the parameter is a completely known and has a constant value (i.e. number of fins)\n",
    " - the parameter can assume certain discrete values with uniform distribution (i.e. the member of an ensemble forecast, which might be any integer from 0 to 9)\n",
    " - the parameter is best represented by a normal (gaussian) distribution with a defined expected value and standard deviation\n",
    "\n",
    "We implement this using a dictionary, where the key is the name of the parameter and the value is either a tuple or a list, depending on the behaviour of the parameter:\n",
    " - if the parameter is know, its value is represented as a list with a single entry (i.e. `\"number_of_fins: [4]\"`)\n",
    " - if the parameter can assume certain discrete values with uniform distribution, its values are represented by a list of possible choices (i.e. `\"member_of_ensemble_forecast: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\"`)\n",
    " - if the parameter is best represented by a normal (gaussian) distribution, its value is a tuple with the expected value and its standard deviation (i.e. `\"rocket_mass\": (100, 2)`, where 100 kg is the expected mass, with uncertainty of plus or minus 2 kg)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "fwoCdOgKpOQD"
   },
   "outputs": [],
   "source": [
    "analysis_parameters = {\n",
    "    # Mass Details\n",
    "    \"rocketMass\": (\n",
    "        8.257,\n",
    "        0.001,\n",
    "    ),  # Rocket's dry mass (kg) and its uncertainty (standard deviation)\n",
    "    # Propulsion Details - run help(SolidMotor) for more information\n",
    "    \"impulse\": [1e-8],  # Motor total impulse (N*s)\n",
    "    \"burn_time\": [1e-8],  # Motor burn out time (s)\n",
    "    \"nozzle_radius\": (21.642 / 1000, 0.5 / 1000),  # Motor's nozzle radius (m)\n",
    "    \"throat_radius\": (8 / 1000, 0.5 / 1000),  # Motor's nozzle throat radius (m)\n",
    "    \"grain_separation\": (\n",
    "        6 / 1000,\n",
    "        1 / 1000,\n",
    "    ),  # Motor's grain separation (axial distance between two grains) (m)\n",
    "    \"grainDensity\": [1e-8],  # Motor's grain density (kg/m^3)\n",
    "    \"grainOuterRadius\": (21.4 / 1000, 0.375 / 1000),  # Motor's grain outer radius (m)\n",
    "    \"grainInitialInnerRadius\": (\n",
    "        9.65 / 1000,\n",
    "        0.375 / 1000,\n",
    "    ),  # Motor's grain inner radius (m)\n",
    "    \"grainInitialHeight\": (120 / 1000, 1 / 1000),  # Motor's grain height (m)\n",
    "    # Aerodynamic Details - run help(Rocket) for more information\n",
    "    \"inertiaI\": (\n",
    "        3.675,\n",
    "        0.03675,\n",
    "    ),  # Rocket's inertia moment perpendicular to its axis (kg*m^2)\n",
    "    \"inertiaZ\": (\n",
    "        0.007,\n",
    "        0.00007,\n",
    "    ),  # Rocket's inertia moment relative to its axis (kg*m^2)\n",
    "    \"radius\": (40.45 / 1000, 0.001),  # Rocket's radius (kg*m^2)\n",
    "    \"distanceRocketNozzle\": (\n",
    "        -1.024,\n",
    "        0.001,\n",
    "    ),  # Distance between rocket's center of dry mass and nozzle exit plane (m) (negative)\n",
    "    \"distanceRocketPropellant\": (\n",
    "        -0.571,\n",
    "        0.001,\n",
    "    ),  # Distance between rocket's center of dry mass and and center of propellant mass (m) (negative)\n",
    "    \"powerOffDrag\": (\n",
    "        0.9081 / 1.05,\n",
    "        0.033,\n",
    "    ),  # Multiplier for rocket's drag curve. Usually has a mean value of 1 and a uncertainty of 5% to 10%\n",
    "    \"powerOnDrag\": (\n",
    "        0.9081 / 1.05,\n",
    "        0.033,\n",
    "    ),  # Multiplier for rocket's drag curve. Usually has a mean value of 1 and a uncertainty of 5% to 10%\n",
    "    \"noseLength\": (0.274, 0.001),  # Rocket's nose cone length (m)\n",
    "    \"noseDistanceToCM\": (\n",
    "        1.134,\n",
    "        0.001,\n",
    "    ),  # Axial distance between rocket's center of dry mass and nearest point in its nose cone (m)\n",
    "    \"finSpan\": (0.077, 0.0005),  # Fin span (m)\n",
    "    \"finRootChord\": (0.058, 0.0005),  # Fin root chord (m)\n",
    "    \"finTipChord\": (0.018, 0.0005),  # Fin tip chord (m)\n",
    "    \"finDistanceToCM\": (\n",
    "        -0.906,\n",
    "        0.001,\n",
    "    ),  # Axial distance between rocket's center of dry mass and nearest point in its fin (m)\n",
    "    # Launch and Environment Details - run help(Environment) and help(Flight) for more information\n",
    "    \"inclination\": (\n",
    "        84.7,\n",
    "        1,\n",
    "    ),  # Launch rail inclination angle relative to the horizontal plane (degrees)\n",
    "    \"heading\": (53, 2),  # Launch rail heading relative to north (degrees)\n",
    "    \"railLength\": (5.7, 0.0005),  # Launch rail length (m)\n",
    "    \"ensembleMember\": [4],  # Members of the ensemble forecast to be used\n",
    "    # Parachute Details - run help(Rocket) for more information\n",
    "    \"CdSDrogue\": (\n",
    "        0.349 * 1.3,\n",
    "        0.07,\n",
    "    ),  # Drag coefficient times reference area for the drogue chute (m^2)\n",
    "    \"lag_rec\": (\n",
    "        1,\n",
    "        0.5,\n",
    "    ),  # Time delay between parachute ejection signal is detected and parachute is inflated (s)\n",
    "    # Electronic Systems Details - run help(Rocket) for more information\n",
    "    \"lag_se\": (\n",
    "        0.73,\n",
    "        1.0,\n",
    "    ),  # Time delay between sensor signal is received and ejection signal is fired (s)\n",
    "}"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "EJCbP69TpOQG"
   },
   "source": [
    "## Creating a Flight Settings Generator\n",
    "\n",
    "Now, we create a generator function which will yield all the necessary inputs for a single flight simulation. Each generated input will be randomly generated according to the `analysis_parameters` dicitionary set up above.\n",
    "\n",
    "This is just a helper function to make the code clearer."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "5XCL9JaIpOQH"
   },
   "outputs": [],
   "source": [
    "def flight_settings(analysis_parameters, total_number):\n",
    "    i = 0\n",
    "    while i < total_number:\n",
    "        # Generate a flight setting\n",
    "        flight_setting = {}\n",
    "        for parameter_key, parameter_value in analysis_parameters.items():\n",
    "            if type(parameter_value) is tuple:\n",
    "                flight_setting[parameter_key] = normal(*parameter_value)\n",
    "            else:\n",
    "                flight_setting[parameter_key] = choice(parameter_value)\n",
    "\n",
    "        # Skip if certain values are negative, which happens due to the normal curve but isnt realistic\n",
    "        if flight_setting[\"lag_rec\"] < 0 or flight_setting[\"lag_se\"] < 0:\n",
    "            continue\n",
    "\n",
    "        # Update counter\n",
    "        i += 1\n",
    "        # Yield a flight setting\n",
    "        yield flight_setting"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "FtdRXCVHpOQO"
   },
   "source": [
    "## Creating an Export Function\n",
    "\n",
    "Monte Carlo analyses usually contain data from thousands or tens of thousands of simulations. They can easily take hours to run. Therefore, it is very important to save our outputs to a file during the analysis. This way, if something happens, we do not lose our progress.\n",
    "\n",
    "These next functions take care of that. They export the simulation data to three different files:\n",
    "- `dispersion_input_file`: A file where each line is a json converted dictionary of flight setting inputs to run a single trajectory simulation;\n",
    "- `dispersion_output_file`: A file where each line is a json converted dictionary containing the main outputs of a single simulation, such as apogee altitute and maximum velocity;\n",
    "- `dispersion_error_file`: A file to store the inputs of simulations which raised errors. This can help us debug these simulations later on."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "id": "1eC2p3jEpOQO"
   },
   "outputs": [],
   "source": [
    "def export_flight_data(flight_setting, flight_data, exec_time):\n",
    "    # Generate flight results\n",
    "    flight_result = {\n",
    "        \"outOfRailTime\": flight_data.outOfRailTime,\n",
    "        \"outOfRailVelocity\": flight_data.outOfRailVelocity,\n",
    "        \"apogeeTime\": flight_data.apogeeTime,\n",
    "        \"apogeeAltitude\": flight_data.apogee - Env.elevation,\n",
    "        \"apogeeX\": flight_data.apogeeX,\n",
    "        \"apogeeY\": flight_data.apogeeY,\n",
    "        \"impactTime\": flight_data.tFinal,\n",
    "        \"impactX\": flight_data.xImpact,\n",
    "        \"impactY\": flight_data.yImpact,\n",
    "        \"impactVelocity\": flight_data.impactVelocity,\n",
    "        \"initialStaticMargin\": flight_data.rocket.staticMargin(0),\n",
    "        \"outOfRailStaticMargin\": flight_data.rocket.staticMargin(\n",
    "            TestFlight.outOfRailTime\n",
    "        ),\n",
    "        \"finalStaticMargin\": flight_data.rocket.staticMargin(\n",
    "            TestFlight.rocket.motor.burnOutTime\n",
    "        ),\n",
    "        \"numberOfEvents\": len(flight_data.parachuteEvents),\n",
    "        \"executionTime\": exec_time,\n",
    "    }\n",
    "\n",
    "    # Calculate maximum reached velocity\n",
    "    sol = np.array(flight_data.solution)\n",
    "    flight_data.vx = Function(\n",
    "        sol[:, [0, 4]], \"Time (s)\", \"Vx (m/s)\", \"linear\", extrapolation=\"natural\"\n",
    "    )\n",
    "    flight_data.vy = Function(\n",
    "        sol[:, [0, 5]], \"Time (s)\", \"Vy (m/s)\", \"linear\", extrapolation=\"natural\"\n",
    "    )\n",
    "    flight_data.vz = Function(\n",
    "        sol[:, [0, 6]], \"Time (s)\", \"Vz (m/s)\", \"linear\", extrapolation=\"natural\"\n",
    "    )\n",
    "    flight_data.v = (flight_data.vx**2 + flight_data.vy**2 + flight_data.vz**2) ** 0.5\n",
    "    flight_data.maxVel = np.amax(flight_data.v.source[:, 1])\n",
    "    flight_result[\"maxVelocity\"] = flight_data.maxVel\n",
    "\n",
    "    # Take care of parachute results\n",
    "    if len(flight_data.parachuteEvents) > 0:\n",
    "        flight_result[\"drogueTriggerTime\"] = flight_data.parachuteEvents[0][0]\n",
    "        flight_result[\"drogueInflatedTime\"] = (\n",
    "            flight_data.parachuteEvents[0][0] + flight_data.parachuteEvents[0][1].lag\n",
    "        )\n",
    "        flight_result[\"drogueInflatedVelocity\"] = flight_data.v(\n",
    "            flight_data.parachuteEvents[0][0] + flight_data.parachuteEvents[0][1].lag\n",
    "        )\n",
    "    else:\n",
    "        flight_result[\"drogueTriggerTime\"] = 0\n",
    "        flight_result[\"drogueInflatedTime\"] = 0\n",
    "        flight_result[\"drogueInflatedVelocity\"] = 0\n",
    "\n",
    "    # Write flight setting and results to file\n",
    "    dispersion_input_file.write(str(flight_setting) + \"\\n\")\n",
    "    dispersion_output_file.write(str(flight_result) + \"\\n\")\n",
    "\n",
    "\n",
    "def export_flight_error(flight_setting):\n",
    "    dispersion_error_file.write(str(flight_setting) + \"\\n\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "GX6pB4Y7pOQQ"
   },
   "source": [
    "## Simulating Each Flight Setting\n",
    "\n",
    "Finally, we can start running some simulations!\n",
    "\n",
    "We start by defining the file name we want to use. Then, we specifiy how many simulations we would like to run by setting the `number_of_simulations` variable.\n",
    "\n",
    "It is good practice to run something in the order of 100 simulations first, to check for any possible errors in the code. Once we are confident that everything is working well, we increase the number of simulations to something in the range of 5000 to 50000.\n",
    "\n",
    "We will loop throught all flight settings, creating the environment, rocket and motor classes with the data of the analysis parameters.\n",
    "For the power off and on drag and thrust curve user should have in hands the .csv (or .eng for comercial motor's thrust curve).\n",
    "\n",
    "**Tip**: A better practice is openning the files in \"append\" mode, this way we can acumulate our simulations. To do this, just change the 'a' (write) argument of the `open` function in the third, fourth and fifth line of code to `a` (append)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 34
    },
    "id": "GILiaO30pOQS",
    "outputId": "5a2ae15d-5c16-4ae0-f28b-165730d2419d"
   },
   "outputs": [],
   "source": [
    "# Basic analysis info\n",
    "filename = \"monte_carlo_analysis_outputs/parachute_drop_from_helicopter\"\n",
    "number_of_simulations = 4000\n",
    "\n",
    "# Create data files for inputs, outputs and error logging\n",
    "dispersion_error_file = open(str(filename) + \".disp_errors.txt\", \"w\")\n",
    "dispersion_input_file = open(str(filename) + \".disp_inputs.txt\", \"w\")\n",
    "dispersion_output_file = open(str(filename) + \".disp_outputs.txt\", \"w\")\n",
    "\n",
    "# Initialize counter and timer\n",
    "i = 0\n",
    "\n",
    "initial_wall_time = time()\n",
    "initial_cpu_time = process_time()\n",
    "\n",
    "# Define basic Environment object\n",
    "Env = Environment(date=(2019, 8, 10, 21), latitude=-23.363611, longitude=-48.011389)\n",
    "Env.setElevation(668)\n",
    "Env.maxExpectedHeight = 1500\n",
    "Env.setAtmosphericModel(\n",
    "    type=\"Ensemble\",\n",
    "    file=\"monte_carlo_analysis_inputs/LASC2019_reanalysis.nc\",\n",
    "    dictionary=\"ECMWF\",\n",
    ")\n",
    "\n",
    "\n",
    "# Set up parachutes. This rocket, named Valetudo, only has a drogue chute.\n",
    "def drogueTrigger(p, h, y):\n",
    "    # Check if rocket is going down, i.e. if it has passed the apogee\n",
    "    vertical_velocity = y[5]\n",
    "    # Return true to activate parachute once the vertical velocity is negative\n",
    "    return True if vertical_velocity < 0 else False\n",
    "\n",
    "\n",
    "# Iterate over flight settings\n",
    "out = display(\"Starting\", display_id=True)\n",
    "for setting in flight_settings(analysis_parameters, number_of_simulations):\n",
    "    start_time = process_time()\n",
    "    i += 1\n",
    "\n",
    "    # Update environment object\n",
    "    Env.selectEnsembleMember(setting[\"ensembleMember\"])\n",
    "\n",
    "    # Create motor\n",
    "    Keron = SolidMotor(\n",
    "        thrustSource=\"monte_carlo_analysis_inputs/thrustCurve.csv\",\n",
    "        burn_time=5.274,\n",
    "        reshapeThrustCurve=(setting[\"burn_time\"], setting[\"impulse\"]),\n",
    "        nozzle_radius=setting[\"nozzle_radius\"],\n",
    "        throat_radius=setting[\"throat_radius\"],\n",
    "        grainsCenterOfMassPosition=setting[\"distanceRocketPropellant\"],\n",
    "        grainNumber=6,\n",
    "        grain_separation=setting[\"grain_separation\"],\n",
    "        grainDensity=setting[\"grainDensity\"],\n",
    "        grainOuterRadius=setting[\"grainOuterRadius\"],\n",
    "        grainInitialInnerRadius=setting[\"grainInitialInnerRadius\"],\n",
    "        grainInitialHeight=setting[\"grainInitialHeight\"],\n",
    "        interpolationMethod=\"linear\",\n",
    "        nozzlePosition=setting[\"distanceRocketNozzle\"],\n",
    "        coordinateSystemOrientation=\"nozzleToCombustionChamber\",\n",
    "    )\n",
    "\n",
    "    # Create rocket\n",
    "    Valetudo = Rocket(\n",
    "        radius=setting[\"radius\"],\n",
    "        mass=setting[\"rocketMass\"],\n",
    "        inertiaI=setting[\"inertiaI\"],\n",
    "        inertiaZ=setting[\"inertiaZ\"],\n",
    "        powerOffDrag=\"monte_carlo_analysis_inputs/Cd_PowerOff.csv\",\n",
    "        powerOnDrag=\"monte_carlo_analysis_inputs/Cd_PowerOn.csv\",\n",
    "        centerOfDryMassPosition=0,\n",
    "        coordinateSystemOrientation=\"tailToNose\",\n",
    "    )\n",
    "    Valetudo.setRailButtons(0.224, -0.93, 30)\n",
    "\n",
    "    Valetudo.addMotor(Keron, position=setting[\"distanceRocketNozzle\"])\n",
    "\n",
    "    # Edit rocket drag\n",
    "    Valetudo.powerOffDrag *= setting[\"powerOffDrag\"]\n",
    "    Valetudo.powerOnDrag *= setting[\"powerOnDrag\"]\n",
    "    # Add rocket nose, fins and tail\n",
    "    NoseCone = Valetudo.addNose(\n",
    "        length=setting[\"noseLength\"],\n",
    "        kind=\"vonKarman\",\n",
    "        position=setting[\"noseDistanceToCM\"] + setting[\"noseLength\"],\n",
    "    )\n",
    "    FinSet = Valetudo.addTrapezoidalFins(\n",
    "        n=3,\n",
    "        span=setting[\"finSpan\"],\n",
    "        rootChord=setting[\"finRootChord\"],\n",
    "        tipChord=setting[\"finTipChord\"],\n",
    "        position=setting[\"finDistanceToCM\"],\n",
    "        cantAngle=0,\n",
    "        airfoil=None,\n",
    "    )\n",
    "    # Add parachute\n",
    "    Drogue = Valetudo.addParachute(\n",
    "        \"Drogue\",\n",
    "        CdS=setting[\"CdSDrogue\"],\n",
    "        trigger=drogueTrigger,\n",
    "        samplingRate=105,\n",
    "        lag=setting[\"lag_rec\"] + setting[\"lag_se\"],\n",
    "        noise=(0, 8.3, 0.5),\n",
    "    )\n",
    "\n",
    "    # Run trajectory simulation\n",
    "    try:\n",
    "        TestFlight = Flight(\n",
    "            rocket=Valetudo,\n",
    "            environment=Env,\n",
    "            railLength=setting[\"railLength\"],\n",
    "            inclination=setting[\"inclination\"],\n",
    "            heading=setting[\"heading\"],\n",
    "            maxTime=600,\n",
    "            initialSolution=[\n",
    "                0.0,\n",
    "                0.0,\n",
    "                0.0,\n",
    "                800 + Env.elevation,\n",
    "                10.0,\n",
    "                10.0,\n",
    "                1.0,\n",
    "                1.0,\n",
    "                0.0,\n",
    "                0.0,\n",
    "                0.0,\n",
    "                0.0,\n",
    "                0.0,\n",
    "                0.0,\n",
    "            ],\n",
    "        )\n",
    "        export_flight_data(setting, TestFlight, process_time() - start_time)\n",
    "    except Exception as E:\n",
    "        print(E)\n",
    "        export_flight_error(setting)\n",
    "\n",
    "    # Register time\n",
    "    out.update(\n",
    "        f\"Curent iteration: {i:06d} | Average Time per Iteration: {(process_time() - initial_cpu_time) / i:2.6f} s\"\n",
    "    )\n",
    "\n",
    "# Done\n",
    "\n",
    "## Print and save total time\n",
    "final_string = f\"Completed {i} iterations successfully. Total CPU time: {process_time() - initial_cpu_time} s. Total wall time: {time() - initial_wall_time} s\"\n",
    "out.update(final_string)\n",
    "dispersion_input_file.write(final_string + \"\\n\")\n",
    "dispersion_output_file.write(final_string + \"\\n\")\n",
    "dispersion_error_file.write(final_string + \"\\n\")\n",
    "\n",
    "## Close files\n",
    "dispersion_input_file.close()\n",
    "dispersion_output_file.close()\n",
    "dispersion_error_file.close()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Post-processing Monte Carlo Dispersion Results\n",
    "\n",
    "Now that we have finish running thousands of simulations, it is time to process the results and get some nice graphs out of them! "
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "X8ewOccUpOQb"
   },
   "source": [
    "## Importing Dispersion Analysis Saved Data\n",
    "\n",
    "We start by loading the file which stores the outputs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "-7qgTJzRpOQb",
    "outputId": "76d2cecd-a09f-429f-cca2-f4e03e39d49e"
   },
   "outputs": [],
   "source": [
    "filename = \"monte_carlo_analysis_outputs/parachute_drop_from_helicopter\"\n",
    "\n",
    "# Initialize variable to store all results\n",
    "dispersion_general_results = []\n",
    "\n",
    "dispersion_results = {\n",
    "    \"outOfRailTime\": [],\n",
    "    \"outOfRailVelocity\": [],\n",
    "    \"apogeeTime\": [],\n",
    "    \"apogeeAltitude\": [],\n",
    "    \"apogeeX\": [],\n",
    "    \"apogeeY\": [],\n",
    "    \"impactTime\": [],\n",
    "    \"impactX\": [],\n",
    "    \"impactY\": [],\n",
    "    \"impactVelocity\": [],\n",
    "    \"initialStaticMargin\": [],\n",
    "    \"outOfRailStaticMargin\": [],\n",
    "    \"finalStaticMargin\": [],\n",
    "    \"numberOfEvents\": [],\n",
    "    \"maxVelocity\": [],\n",
    "    \"drogueTriggerTime\": [],\n",
    "    \"drogueInflatedTime\": [],\n",
    "    \"drogueInflatedVelocity\": [],\n",
    "    \"executionTime\": [],\n",
    "}\n",
    "\n",
    "# Get all dispersion results\n",
    "# Get file\n",
    "dispersion_output_file = open(str(filename) + \".disp_outputs.txt\", \"r+\")\n",
    "\n",
    "# Read each line of the file and convert to dict\n",
    "for line in dispersion_output_file:\n",
    "    # Skip comments lines\n",
    "    if line[0] != \"{\":\n",
    "        continue\n",
    "    # Eval results and store them\n",
    "    flight_result = eval(line)\n",
    "    dispersion_general_results.append(flight_result)\n",
    "    for parameter_key, parameter_value in flight_result.items():\n",
    "        dispersion_results[parameter_key].append(parameter_value)\n",
    "\n",
    "# Close data file\n",
    "dispersion_output_file.close()\n",
    "\n",
    "# Print number of flights simulated\n",
    "N = len(dispersion_general_results)\n",
    "print(\"Number of simulations: \", N)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "ioeUkzPipOQe"
   },
   "source": [
    "## Dispersion Results\n",
    "\n",
    "Now, we plot the histogram for every single output. This shows how are outputs behave. Valuable statistical data can be calculated based on them."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "tExJzLhDpOQp"
   },
   "source": [
    "### Apogee Time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "l8zjT_VjpOQq",
    "outputId": "1c15fe12-afae-4035-f085-7d82e61d24d9"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Apogee Time -         Mean Value: {np.mean(dispersion_results['apogeeTime']):0.3f} s\"\n",
    ")\n",
    "print(\n",
    "    f\"Apogee Time - Standard Deviation: {np.std(dispersion_results['apogeeTime']):0.3f} s\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"apogeeTime\"], bins=int(N**0.5))\n",
    "plt.title(\"Apogee Time\")\n",
    "plt.xlabel(\"Time (s)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "erNB46vApOQt"
   },
   "source": [
    "### Apogee Altitude"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "gWWMoOClpOQv",
    "outputId": "88f2cf05-142c-4bb1-ce64-9879696107a7"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Apogee Altitude -         Mean Value: {np.mean(dispersion_results['apogeeAltitude']):0.3f} m\"\n",
    ")\n",
    "print(\n",
    "    f\"Apogee Altitude - Standard Deviation: {np.std(dispersion_results['apogeeAltitude']):0.3f} m\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"apogeeAltitude\"], bins=int(N**0.5))\n",
    "plt.title(\"Apogee Altitude\")\n",
    "plt.xlabel(\"Altitude (m)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()\n",
    "\n",
    "# Real measured apogee for Valetudo = 860 m"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "7bBvFJ5xpOQ1"
   },
   "source": [
    "### Apogee X Position"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "nGdsF9VppOQ3",
    "outputId": "b4f0a3aa-afa1-4942-91b8-8ad61d263244"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Apogee X Position -         Mean Value: {np.mean(dispersion_results['apogeeX']):0.3f} m\"\n",
    ")\n",
    "print(\n",
    "    f\"Apogee X Position - Standard Deviation: {np.std(dispersion_results['apogeeX']):0.3f} m\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"apogeeX\"], bins=int(N**0.5))\n",
    "plt.title(\"Apogee X Position\")\n",
    "plt.xlabel(\"Apogee X Position (m)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "-LtYxB0lpOQ8"
   },
   "source": [
    "### Apogee Y Position"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "ocq6GmeNpOQ8",
    "outputId": "f3a45339-c0a6-4819-bd03-60f7fe6df963"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Apogee Y Position -         Mean Value: {np.mean(dispersion_results['apogeeY']):0.3f} m\"\n",
    ")\n",
    "print(\n",
    "    f\"Apogee Y Position - Standard Deviation: {np.std(dispersion_results['apogeeY']):0.3f} m\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"apogeeY\"], bins=int(N**0.5))\n",
    "plt.title(\"Apogee Y Position\")\n",
    "plt.xlabel(\"Apogee Y Position (m)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "ifuJX7jYpORB"
   },
   "source": [
    "### Impact Time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "52j6t5-MpORB",
    "outputId": "9cba31b1-c731-402f-b138-df5c72521408"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Impact Time -         Mean Value: {np.mean(dispersion_results['impactTime']):0.3f} s\"\n",
    ")\n",
    "print(\n",
    "    f\"Impact Time - Standard Deviation: {np.std(dispersion_results['impactTime']):0.3f} s\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"impactTime\"], bins=int(N**0.5))\n",
    "plt.title(\"Impact Time\")\n",
    "plt.xlabel(\"Time (s)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "mYD4EQ5spORE"
   },
   "source": [
    "### Impact X Position"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "uzL8-1UGpORF",
    "outputId": "5c74f8d1-b909-44cf-a5c9-2f1b5e9dda1d"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Impact X Position -         Mean Value: {np.mean(dispersion_results['impactX']):0.3f} m\"\n",
    ")\n",
    "print(\n",
    "    f\"Impact X Position - Standard Deviation: {np.std(dispersion_results['impactX']):0.3f} m\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"impactX\"], bins=int(N**0.5))\n",
    "plt.title(\"Impact X Position\")\n",
    "plt.xlabel(\"Impact X Position (m)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "ajI4vr7QpORL"
   },
   "source": [
    "### Impact Y Position"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "Q-ghmNVopORM",
    "outputId": "cd0c81a8-a3fc-4710-cadf-a20cf0882bec"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Impact Y Position -         Mean Value: {np.mean(dispersion_results['impactY']):0.3f} m\"\n",
    ")\n",
    "print(\n",
    "    f\"Impact Y Position - Standard Deviation: {np.std(dispersion_results['impactY']):0.3f} m\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"impactY\"], bins=int(N**0.5))\n",
    "plt.title(\"Impact Y Position\")\n",
    "plt.xlabel(\"Impact Y Position (m)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "H7_H_eeXpORP"
   },
   "source": [
    "### Impact Velocity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "Ryx7KEEVpORP",
    "outputId": "90cdcf97-affc-4f09-ed2a-9b854257a8a0"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Impact Velocity -         Mean Value: {np.mean(dispersion_results['impactVelocity']):0.3f} m/s\"\n",
    ")\n",
    "print(\n",
    "    f\"Impact Velocity - Standard Deviation: {np.std(dispersion_results['impactVelocity']):0.3f} m/s\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"impactVelocity\"], bins=int(N**0.5))\n",
    "plt.title(\"Impact Velocity\")\n",
    "# plt.grid()\n",
    "plt.xlim(-35, 0)\n",
    "plt.xlabel(\"Velocity (m/s)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "mQzQELcJpORX"
   },
   "source": [
    "### Maximum Velocity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "nOu1O8MXpORY",
    "outputId": "7510aec8-7b73-4751-f033-8367cd0c9bdc"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Maximum Velocity -         Mean Value: {np.mean(dispersion_results['maxVelocity']):0.3f} m/s\"\n",
    ")\n",
    "print(\n",
    "    f\"Maximum Velocity - Standard Deviation: {np.std(dispersion_results['maxVelocity']):0.3f} m/s\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"maxVelocity\"], bins=int(N**0.5))\n",
    "plt.title(\"Maximum Velocity\")\n",
    "plt.xlabel(\"Velocity (m/s)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "7MUVLAM-pORb"
   },
   "source": [
    "### Number of Parachute Events\n",
    "\n",
    "This is usefull to check if the parachute was triggered in every flight."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "yhcWi2kCpORb",
    "outputId": "dee54569-8213-46cc-e287-9fffd2b8e43c"
   },
   "outputs": [],
   "source": [
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"numberOfEvents\"])\n",
    "plt.title(\"Parachute Events\")\n",
    "plt.xlabel(\"Number of Parachute Events\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "4LpDYGpfpORf"
   },
   "source": [
    "### Drogue Parachute Trigger Time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "lvCksZG8pORf",
    "outputId": "3efd19ed-11e9-41d1-8e66-04da384665ce"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Drogue Parachute Trigger Time -         Mean Value: {np.mean(dispersion_results['drogueTriggerTime']):0.3f} s\"\n",
    ")\n",
    "print(\n",
    "    f\"Drogue Parachute Trigger Time - Standard Deviation: {np.std(dispersion_results['drogueTriggerTime']):0.3f} s\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"drogueTriggerTime\"], bins=int(N**0.5))\n",
    "plt.title(\"Drogue Parachute Trigger Time\")\n",
    "plt.xlabel(\"Time (s)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "nKSzDWi7pORi"
   },
   "source": [
    "### Drogue Parachute Fully Inflated Time"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "KCYKVFYXpORj",
    "outputId": "8ae49853-0f08-4bf6-9bd8-91a7c9a9448d"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Drogue Parachute Fully Inflated Time -         Mean Value: {np.mean(dispersion_results['drogueInflatedTime']):0.3f} s\"\n",
    ")\n",
    "print(\n",
    "    f\"Drogue Parachute Fully Inflated Time - Standard Deviation: {np.std(dispersion_results['drogueInflatedTime']):0.3f} s\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"drogueInflatedTime\"], bins=int(N**0.5))\n",
    "plt.title(\"Drogue Parachute Fully Inflated Time\")\n",
    "plt.xlabel(\"Time (s)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "BvMCGZYHpORn"
   },
   "source": [
    "### Drogue Parachute Fully Inflated Velocity"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 607
    },
    "id": "KxrpPUzqpORn",
    "outputId": "9c102bdb-b805-4aa1-b3a2-1ac329c76976"
   },
   "outputs": [],
   "source": [
    "print(\n",
    "    f\"Drogue Parachute Fully Inflated Velocity -         Mean Value: {np.mean(dispersion_results['drogueInflatedVelocity']):0.3f} m/s\"\n",
    ")\n",
    "print(\n",
    "    f\"Drogue Parachute Fully Inflated Velocity - Standard Deviation: {np.std(dispersion_results['drogueInflatedVelocity']):0.3f} m/s\"\n",
    ")\n",
    "\n",
    "plt.figure()\n",
    "plt.hist(dispersion_results[\"drogueInflatedVelocity\"], bins=int(N**0.5))\n",
    "plt.title(\"Drogue Parachute Fully Inflated Velocity\")\n",
    "plt.xlabel(\"Velocity m/s)\")\n",
    "plt.ylabel(\"Number of Occurences\")\n",
    "plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {
    "id": "TlWXtnKMrlMI"
   },
   "source": [
    "### Error Ellipses\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "both",
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 837
    },
    "id": "DZRrk_bIr3iG",
    "outputId": "b4a5a583-7f53-473a-bec2-2636cdf28a7c"
   },
   "outputs": [],
   "source": [
    "# Import libraries\n",
    "from imageio import imread\n",
    "from matplotlib.patches import Ellipse\n",
    "\n",
    "# Import background map\n",
    "img = imread(\"monte_carlo_analysis_inputs/Valetudo_basemap_final.jpg\")\n",
    "\n",
    "# Retrieve dispersion data por apogee and impact XY position\n",
    "apogeeX = np.array(dispersion_results[\"apogeeX\"])\n",
    "apogeeY = np.array(dispersion_results[\"apogeeY\"])\n",
    "impactX = np.array(dispersion_results[\"impactX\"])\n",
    "impactY = np.array(dispersion_results[\"impactY\"])\n",
    "\n",
    "\n",
    "# Define function to calculate eigen values\n",
    "def eigsorted(cov):\n",
    "    vals, vecs = np.linalg.eigh(cov)\n",
    "    order = vals.argsort()[::-1]\n",
    "    return vals[order], vecs[:, order]\n",
    "\n",
    "\n",
    "# Create plot figure\n",
    "plt.figure(num=None, figsize=(8, 6), dpi=150, facecolor=\"w\", edgecolor=\"k\")\n",
    "ax = plt.subplot(111)\n",
    "\n",
    "# Calculate error ellipses for impact\n",
    "impactCov = np.cov(impactX, impactY)\n",
    "impactVals, impactVecs = eigsorted(impactCov)\n",
    "impactTheta = np.degrees(np.arctan2(*impactVecs[:, 0][::-1]))\n",
    "impactW, impactH = 2 * np.sqrt(impactVals)\n",
    "\n",
    "# Draw error ellipses for impact\n",
    "impact_ellipses = []\n",
    "for j in [1, 2, 3]:\n",
    "    impactEll = Ellipse(\n",
    "        xy=(np.mean(impactX), np.mean(impactY)),\n",
    "        width=impactW * j,\n",
    "        height=impactH * j,\n",
    "        angle=impactTheta,\n",
    "        color=\"black\",\n",
    "    )\n",
    "    impactEll.set_facecolor((0, 0, 1, 0.2))\n",
    "    impact_ellipses.append(impactEll)\n",
    "    ax.add_artist(impactEll)\n",
    "\n",
    "# Calculate error ellipses for apogee\n",
    "apogeeCov = np.cov(apogeeX, apogeeY)\n",
    "apogeeVals, apogeeVecs = eigsorted(apogeeCov)\n",
    "apogeeTheta = np.degrees(np.arctan2(*apogeeVecs[:, 0][::-1]))\n",
    "apogeeW, apogeeH = 2 * np.sqrt(apogeeVals)\n",
    "\n",
    "# Draw error ellipses for apogee\n",
    "for j in [1, 2, 3]:\n",
    "    apogeeEll = Ellipse(\n",
    "        xy=(np.mean(apogeeX), np.mean(apogeeY)),\n",
    "        width=apogeeW * j,\n",
    "        height=apogeeH * j,\n",
    "        angle=apogeeTheta,\n",
    "        color=\"black\",\n",
    "    )\n",
    "    apogeeEll.set_facecolor((0, 1, 0, 0.2))\n",
    "    ax.add_artist(apogeeEll)\n",
    "\n",
    "# Draw launch point\n",
    "plt.scatter(0, 0, s=30, marker=\"*\", color=\"black\", label=\"Launch Point\")\n",
    "# Draw apogee points\n",
    "plt.scatter(apogeeX, apogeeY, s=5, marker=\"^\", color=\"green\", label=\"Simulated Apogee\")\n",
    "# Draw impact points\n",
    "plt.scatter(\n",
    "    impactX, impactY, s=5, marker=\"v\", color=\"blue\", label=\"Simulated Landing Point\"\n",
    ")\n",
    "\n",
    "plt.legend()\n",
    "\n",
    "# Add title and labels to plot\n",
    "ax.set_title(\n",
    "    \"1$\\sigma$, 2$\\sigma$ and 3$\\sigma$ Dispersion Ellipses: Apogee and Lading Points\"\n",
    ")\n",
    "ax.set_ylabel(\"North (m)\")\n",
    "ax.set_xlabel(\"East (m)\")\n",
    "\n",
    "# Add background image to plot\n",
    "# You can translate the basemap by changing dx and dy (in meters)\n",
    "dx = 0\n",
    "dy = 0\n",
    "plt.imshow(img, zorder=0, extent=[-1000 - dx, 1000 - dx, -1000 - dy, 1000 - dy])\n",
    "plt.axhline(0, color=\"black\", linewidth=0.5)\n",
    "plt.axvline(0, color=\"black\", linewidth=0.5)\n",
    "plt.xlim(100, 400)\n",
    "plt.ylim(-200, 50)\n",
    "\n",
    "# Save plot and show result\n",
    "plt.savefig(str(filename) + \".pdf\", bbox_inches=\"tight\", pad_inches=0)\n",
    "plt.savefig(str(filename) + \".svg\", bbox_inches=\"tight\", pad_inches=0)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [
    "au88RN0P-bVl",
    "-9u9RIaqpOQl",
    "tExJzLhDpOQp",
    "ifuJX7jYpORB",
    "mYD4EQ5spORE",
    "ajI4vr7QpORL",
    "9m19OV9upORS",
    "mQzQELcJpORX",
    "7MUVLAM-pORb",
    "4LpDYGpfpORf",
    "nKSzDWi7pORi",
    "BvMCGZYHpORn",
    "LhPQQlpHpORq",
    "5MvfiSQZvwPK"
   ],
   "name": "Valetudo_Monte_Carlo_Dispersion_Analysis.ipynb",
   "provenance": [],
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Python 3.10.5 64-bit",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.10.5"
  },
  "vscode": {
   "interpreter": {
    "hash": "26de051ba29f2982a8de78e945f0abaf191376122a1563185a90213a26c5da77"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
